package edu.nf.relaxenjoy.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import edu.nf.relaxenjoy.common.constant.recommend.RecommendConstant;
import edu.nf.relaxenjoy.common.recommend.RecommendUtils2;
import edu.nf.relaxenjoy.common.util.ConvertUtils;
import edu.nf.relaxenjoy.common.util.JsonUtils;
import edu.nf.relaxenjoy.common.util.KeyWordsExtrcator;
import edu.nf.relaxenjoy.common.util.RedisUtils;
import edu.nf.relaxenjoy.domain.entity.*;
import edu.nf.relaxenjoy.domain.vo.PostVo;
import edu.nf.relaxenjoy.mapper.*;
import edu.nf.relaxenjoy.service.RecommendService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhu
 * @date 2024-12-20
 */
@Service
@RequiredArgsConstructor
public class RecommendServiceImpl implements RecommendService {

    private final UserMapper userMapper;

    private final CategoryMapper categoryMapper;

    private final TagMapper tagMapper;

    private final TagRelationMapper tagRelationMapper;

    private final RedisUtils redisUtils;

    private final PostMapper postMapper;

    @Override
    public HashMap<String, Object> recommendToUser(long page, long limit, String uid) {
        HashMap<String, Object> resMap = new HashMap<>(2);

        String ukey = RecommendConstant.BR_IMG_KEY + uid;
        if (Boolean.FALSE.equals(redisUtils.hasKey(ukey))) {
            return null;
        }

        //这里获取当前用户的前n条浏览记录(这里只选择一条)
        List<String> mids = redisUtils.lRange(ukey, 0, 2);
        Set<Post> set = new HashSet<>();
        Set<Post> recommendSet;
        for (int i = 0; i < (mids.size() <= 1 ? 1 : 2); i++) {
            recommendSet = getRecommendImgDetails(page, limit, mids.get(i), mids);
            set.addAll(recommendSet);
        }

        List<PostVo> imgDetailVoList = new ArrayList<>();
        if (set.isEmpty()) {
            resMap.put(RecommendConstant.RECORDS, imgDetailVoList);
            resMap.put(RecommendConstant.TOTAL, set.size());
            return resMap;
        }

        PostVo imgDetailVo;
        User user;

        Set<Long> uids = set.stream().map(Post::getUserId).collect(Collectors.toSet());
        List<Long> longs = uids.stream().map(Long::valueOf).toList();
        List<User> userList = userMapper.listByIds(longs);

        HashMap<Long, User> userMap = new HashMap<>();
        userList.forEach(item -> {
            userMap.put(item.getUserId(), item);
        });

        for (Post model : set) {
            imgDetailVo = ConvertUtils.sourceToTarget(model, PostVo.class);
            user = userMap.get(model.getUserId());
            imgDetailVo.setUserId(user.getUserId());
            imgDetailVo.setUsername(user.getUsername());
            imgDetailVo.setAvatar(user.getAvatar());
            imgDetailVoList.add(imgDetailVo);
        }

        resMap.put(RecommendConstant.RECORDS, imgDetailVoList);
        resMap.put(RecommendConstant.TOTAL, set.size());
        return resMap;
    }

    @Override
    public HashMap<String, Object> recommendToUserByCF(long page, long limit, String uid) {
        HashMap<String, Object> resMap = new HashMap<>(2);

        String ukey = RecommendConstant.BR_IMG_KEY + uid;
        if (Boolean.FALSE.equals(redisUtils.hasKey(ukey))) {
            return null;
        }

        //这里获取当前用户的前n条浏览记录(这里只选择一条)
        List<String> ids = redisUtils.lRange(ukey, 0, 5);
        List<Long> mids = new ArrayList<>();
        List<Post> postRecords = new ArrayList<>();
        if (!ids.isEmpty()) {
            mids = ids.stream().map(JSON::parseObject).map(obj -> obj.getLong("id")).toList();
            postRecords = postMapper.listByIds(mids, null);
        }

        String postKey = RecommendConstant.POST_LIST_KEY;
        Map<Object, Object> postMap = redisUtils.hGetAll(postKey);

        //所有的图片
        List<Object> postList = new ArrayList<>(postMap.values());
        List<List<Object>> allPostList = Lists.partition(postList, (int) limit);

        //每一份是50张图片（随机从50张中再选择6组合）
        List<Object> objectRecord = allPostList.get((int) page - 1);

        Collections.shuffle(objectRecord);

        List<Object> selectPostList = objectRecord.subList(0, 6);


        List<Post> posts = new ArrayList<>();
        for (Object element:selectPostList) {
            Post model = JsonUtils.parseObject((String) element, Post.class);
            if(!postRecords.contains(model)){
                posts.add(model);
            }
        }

        if (posts.size() % 2 != 0) {
            // 去掉最后一个元素使其成为偶数
            posts.removeLast();
        }

        List<Long> uids = posts.stream().map(Post::getUserId).toList();
        List<User> userList = userMapper.listByIds(uids);

        HashMap<Long, User> userMap = new HashMap<>();
        userList.forEach(item -> {
            userMap.put(item.getUserId(), item);
        });

        List<PostVo> postVoList = new ArrayList<>();
        PostVo postVo;
        User user;
        for (Post model : posts) {
            postVo = ConvertUtils.sourceToTarget(model, PostVo.class);
            user = userMap.get(model.getUserId());
            postVo.setUserId(user.getUserId());
            postVo.setUsername(user.getUsername());
            postVo.setAvatar(user.getAvatar());
            postVoList.add(postVo);
        }

        resMap.put(RecommendConstant.RECORDS, postVoList);
        resMap.put(RecommendConstant.TOTAL, 24);
        return resMap;
    }

    private Set<Post> getRecommendImgDetails(long page, long limit, String mid, List<String> mids) {
        Set<Post> resSet = new HashSet<>();

        float[] embeddings1 = null;

        String recommendByUserKey = RecommendConstant.RECOMMEND + mid;
        if (Boolean.TRUE.equals(redisUtils.hasKey(recommendByUserKey))) {
            embeddings1 = JsonUtils.parseObject(redisUtils.get(recommendByUserKey), float[].class);
        } else {
            Post postsEntity = postMapper.getById(Long.valueOf(mid));
            List<String> keywords1 = getContent(postsEntity);
            try {
                embeddings1 = RecommendUtils2.getEmbeddings(keywords1);
            } catch (Exception e) {
                e.printStackTrace();
            }
            redisUtils.set(recommendByUserKey, JsonUtils.toJsonString(embeddings1));
        }

        //TODO 选择数据(这里的参数后面都要修改，数据量太少了)
        String imgDetailKey = RecommendConstant.POST_LIST_KEY;
        Map<Object, Object> imgMap = redisUtils.hGetAll(imgDetailKey);

        //所有的图片
        List<Object> imgDetailList = new ArrayList<>(imgMap.values());
        List<List<Object>> allImgDetailList = Lists.partition(imgDetailList, (int) limit);
        //每个里面有50条数据
        List<Object> imgDetailRecord = allImgDetailList.get((int) page - 1);

        if ((int) page >= allImgDetailList.size()) {
            return resSet;
        }

        Collections.shuffle(imgDetailRecord);

        //从20条记录中选择3条
        List<List<Object>> partition = Lists.partition(imgDetailRecord, 20);
        //计算相似性
        Map<Post, Double> map = new HashMap<>();

        for (Object element : partition.get(0)) {
            //计算词向量部分需要的时间过长，修改使用redis存储词向量
            Post model = JsonUtils.parseObject((String) element, Post.class);
            float[] embeddings2 = null;
            String key = RecommendConstant.RECOMMEND + model.getId();
            if (Boolean.TRUE.equals(redisUtils.hasKey(key))) {
                embeddings2 = JsonUtils.parseObject(redisUtils.get(key), float[].class);
            } else {
                List<String> keywords2 = getContent(model);
                try {
                    embeddings2 = RecommendUtils2.getEmbeddings(keywords2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                redisUtils.set(key, JsonUtils.toJsonString(embeddings2));
            }

            Double value = RecommendUtils2.getSimilar(embeddings1, embeddings2);
            map.put(model, value);
        }

        Map<Post, Double> sortedMap = map.entrySet().stream()
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (oldVal, newVal) -> oldVal,
                        LinkedHashMap::new));

        for (Post model : sortedMap.keySet()) {
            if (!mids.contains(String.valueOf(model.getId()))) {
                resSet.add(model);
                if (resSet.size() >= RecommendConstant.SIZE) {
                    break;
                }
            }
        }

        return resSet;

    }


    private List<String> getContent(Post post) {
        StringBuilder sb = new StringBuilder();
        String content = post.getContent();
        sb.append(content);

        List<TagRelation> tagImgRelationList = tagRelationMapper.getList(post.getId());
        List<Long> tids = tagImgRelationList.stream().map(TagRelation::getTid).collect(Collectors.toList());

        if(!tids.isEmpty()){
            List<Tag> tags = tagMapper.listByIds(tids);
            for (Tag tag : tags) {
                sb.append(tag.getName());
            }
        }

        List<Long> cidList = new ArrayList<>(2);
        cidList.add(post.getCategoryPid());
        cidList.add(post.getCategoryId());

        List<Category> categorys = categoryMapper.listByIds(cidList);

        for (Category category : categorys) {
            sb.append(category.getName());
        }
        return KeyWordsExtrcator.getKeywords(sb.toString());
    }
}
